// Material Resource
// (c) jimon game studio

#ifndef JEH_JEMATERIALRESOURCE
#define JEH_JEMATERIALRESOURCE

#include "IResourceManager.h"
#include "jeSharedResource.h"
#include "jeImageResource.h"
#include "jeShaderMaterialResource.h"
#include "jeMaterial.h"

namespace je
{
	namespace media
	{
		#define JE_MAX_MEDIA_MATERIAL_TEXTURES 2

		//! Material Resource
		class jeMaterialResource:public je::media::IResource
		{
		public:
			typedef jeSharedResourceManagerUniversal jeSharedMaterialResourceManager;
		protected:
			video::MaterialType Type;
			jeStringc ShaderMaterial;
			u1 EnableZBufferTest;
			u1 EnableZBufferMask;
			jeStringc Textures[2];

			jeSharedMaterialResourceManager * SharedManager;
		public:
			//! Constructor
			jeMaterialResource(const jeStringc & ResourceName)
				:IResource(ResourceName)
			{
				JENEW(SharedManager,jeSharedMaterialResourceManager)
			}

			//! Constructor
			jeMaterialResource(const jeStringc & ResourceName,video::MaterialType NewType,const jeStringc & NewShaderMaterial,u1 NewEnableZBufferTest,u1 NewEnableZBufferMask,jeStringc * NewTextures)
				:IResource(ResourceName,false),Type(NewType),ShaderMaterial(NewShaderMaterial),EnableZBufferTest(NewEnableZBufferTest),EnableZBufferMask(NewEnableZBufferMask)
			{
				for(s32 i=0;i<JE_MAX_MEDIA_MATERIAL_TEXTURES;i++)
					Textures[i] = NewTextures[i];
				JENEW(SharedManager,jeSharedMaterialResourceManager)
			}

			//! Destructor
			~jeMaterialResource()
			{
				JEDELETE(SharedManager)
			}

			//! Get Shared Manager
			jeSharedMaterialResourceManager * GetSharedManager()
			{
				return SharedManager;
			}

			//! Get Material Type
			video::MaterialType GetMaterialType()
			{
				return Type;
			}

			//! Get Shader Material
			const jeStringc & GetShaderMaterial()
			{
				return ShaderMaterial;
			}

			//! Get EnableZBufferTest
			u1 GetEnableZBufferTest()
			{
				return EnableZBufferTest;
			}

			//! Get EnableZBufferTestMask
			u1 GetEnableZBufferMask()
			{
				return EnableZBufferMask;
			}

			//! Get Texture
			const jeStringc & GetTexture(s32 i)
			{
				return Textures[i];
			}

			//! Load Resource
			void Load()
			{
				core::IDataBase_Query * Query = GetResourceManager()->GetDataBase()->Query(jeStringc("SELECT * FROM MaterialResources WHERE Name = '")+CurrentResourceName+jeStringc("'"));
				Type = (video::MaterialType)Query->GetFields32("Type");
				ShaderMaterial = Query->GetFieldjeStringc("ShaderMaterial");
				EnableZBufferTest = Query->GetFieldu1("EnableZBufferTest");
				EnableZBufferMask = Query->GetFieldu1("EnableZBufferMask");
				for(s32 i=0;i<JE_MAX_MEDIA_MATERIAL_TEXTURES;i++)
					Textures[i] = Query->GetFieldjeStringc(jeStringc("Texture")+jeStringc(i));
				Query->Drop();
			}

			//! UnLoad Resource
			void UnLoad()
			{
			}

			//! Get Type Name
			virtual jeStringc GetTypeName()
			{
				return jeStringc("MaterialResource");
			}
		};

		//! Material Resource Factory
		class jeMaterialResourceFactory:public je::media::IResourceFactory
		{
		protected:
			typedef jeMap<jeStringc,jeMaterialResource*> MaterialMapType;
			MaterialMapType MaterialMap;
		public:

			//! Constructor
			jeMaterialResourceFactory()
			{
			}

			//! Destructor
			~jeMaterialResourceFactory()
			{
				ClearResources();
			}

			//! Add Resources
			void AddResources(IResource * Resource)
			{
				MaterialMap.set(Resource->GetName(),static_cast<jeMaterialResource*>(Resource));
				Resource->Grab();
			}

			//! Load Resources
			void LoadResources()
			{
				ClearResources();
				core::IDataBase_Query * Query = GetResourceManager()->GetDataBase()->Query("SELECT * FROM MaterialResources");
				while(!Query->Eof())
				{
					jeStringc ResourceName = Query->GetFieldjeStringc("Name");
					jeMaterialResource * MaterialResource = NULL;
					JENEW(MaterialResource,jeMaterialResource(ResourceName))
					MaterialMap.set(ResourceName,MaterialResource);
					Query->NextRow();
				}
				Query->Drop();
			}

			//! Clear Resources
			void ClearResources()
			{
				for(MaterialMapType::Iterator Iter = MaterialMap.getIterator();!Iter.atEnd();Iter++)
					Iter->getValue()->Drop();
				MaterialMap.clear();
			}

			//! Return Resource
			IResource * GetResource(const jeStringc & ResourceName)
			{
				MaterialMapType::Node * Node = MaterialMap.find(ResourceName);
				if(Node)
					return Node->getValue();
				else
					return NULL;
			}

			//! Return Resource Type
			jeStringc GetResourceType()
			{
				return jeStringc("MaterialResource");
			}

			//! Garbage Collector
			void GarbageCollector()
			{
				for(MaterialMapType::Iterator Iter = MaterialMap.getIterator();!Iter.atEnd();Iter++)
					if(Iter->getValue()->IsNeedToUnload())
						Iter->getValue()->GarbageUnload();
			}
		};

		//! Shared Material
		class jeSharedMaterial:public media::ISharedResource
		{
		protected:
			video::IRender * CurrentRender;
			jeStringc MaterialResourceName;
			video::jeMaterial * Material;
			jeSharedShaderMaterial * SharedShaderMaterial;
			typedef jeDynamicArray<jeSharedTexture*> TexturesListType;
			TexturesListType Textures;
			u1 CurrentUseUniqueTexture;
			u1 CurrentUseUniqueShader;
		public:
			//! Constructor
			jeSharedMaterial(jeMaterialResource * MaterialResource,video::IRender * Render,u1 UseUniqueTexture,u1 UseUniqueShader)
				:CurrentRender(Render),SharedShaderMaterial(NULL),CurrentUseUniqueTexture(UseUniqueTexture),CurrentUseUniqueShader(UseUniqueShader)
			{
				MaterialResource->AddUsage();
				MaterialResourceName = MaterialResource->GetName();

				JENEW(Material,video::jeMaterial)
				Material->Type = MaterialResource->GetMaterialType();
				Material->EnableZBufferTest = MaterialResource->GetEnableZBufferTest();
				Material->EnableZBufferMask = MaterialResource->GetEnableZBufferMask();

				for(s32 i=0;i<JE_MAX_MEDIA_MATERIAL_TEXTURES;i++)
				{
					media::jeSharedTexture * Texture = NULL;
					if(MaterialResource->GetTexture(i).GetSize())
						if(CurrentUseUniqueTexture)
							JE_IMAGERESOURCE_GRAB(Texture,JE_IMAGERESOURCE(MaterialResource->GetTexture(i)),this,CurrentRender)
						else
							JE_IMAGERESOURCE_SHARED_GRAB(Texture,JE_IMAGERESOURCE(MaterialResource->GetTexture(i)),CurrentRender)
					Textures.InsertElement(Texture);
				}

				for(u32 i=0;i<Textures.GetSize();i++)
					if(Textures[i])
						Material->SetTexture(Textures[i]->GetFirst(),i);

				if(MaterialResource->GetShaderMaterial().GetSize())
				{
					if(CurrentUseUniqueShader)
						JE_SHADERMATERIALRESOURCE_GRAB(SharedShaderMaterial,JE_SHADERMATERIALRESOURCE(MaterialResource->GetShaderMaterial()),this,CurrentRender)
					else
						JE_SHADERMATERIALRESOURCE_SHARED_GRAB(SharedShaderMaterial,JE_SHADERMATERIALRESOURCE(MaterialResource->GetShaderMaterial()),CurrentRender)
					Material->SetShaderMaterial(SharedShaderMaterial->GetShaderMaterial());
				}

				MaterialResource->RemoveUsage();
			}

			//! Destructor
			~jeSharedMaterial()
			{
				JEDELETE(Material)

				for(u32 i=0;i<Textures.GetSize();i++)
					if(Textures[i])
						if(CurrentUseUniqueTexture)
							JE_IMAGERESOURCE_DROP(Textures[i],this,CurrentRender)
						else
							JE_IMAGERESOURCE_SHARED_DROP(Textures[i],CurrentRender)

				if(SharedShaderMaterial)
					if(CurrentUseUniqueShader)
						JE_SHADERMATERIALRESOURCE_DROP(SharedShaderMaterial,this,CurrentRender)
					else
						JE_SHADERMATERIALRESOURCE_SHARED_DROP(SharedShaderMaterial,CurrentRender)
			}

			//! Get Resource Name
			const jeStringc & GetResourceName()
			{
				return MaterialResourceName;
			}

			//! Get Material
			video::jeMaterial * GetMaterial()
			{
				return Material;
			}

			//! Get Shader Material
			jeSharedShaderMaterial * GetShaderMaterial()
			{
				return SharedShaderMaterial;
			}

			//! Get Texture
			jeSharedTexture * GetTexture(s32 i)
			{
				return Textures[i];
			}
		};
	}
}

#define JE_MATERIALRESOURCE( __Name ) JE_RESOURCEBASE_GET(__Name,MaterialResource)

#define JE_MATERIALRESOURCE_GRAB( __Var, __Res, __Key, __UseUniqueTexture, __UseUniqueShader, __Render ) JE_RESOURCEBASE_SHARED_GRAB_FREEKEY(__Var,__Res,__Key,(__Res,__Render,__UseUniqueTexture,__UseUniqueShader),Material)
#define JE_MATERIALRESOURCE_DROP( __Var, __Key, __Render) JE_RESOURCEBASE_SHARED_DROP(__Var,__Key,MaterialResource)

#define JE_MATERIALRESOURCE_SHARED_GRAB( __Var, __Res, __Render ) JE_MATERIALRESOURCE_GRAB(__Var,__Res,__Render->GetContext(),false,false,__Render)
#define JE_MATERIALRESOURCE_SHARED_DROP( __Var, __Render) JE_MATERIALRESOURCE_DROP(__Var,__Render->GetContext(),__Render)

#endif
